(function ( exports ) {
    //region ArrayValueBase

    function ArrayValueBase ( items ) {
        Array.call(this);

        var _this = this;
        if ( typeof items != 'undefined' && items !== null && typeof items.forEach != 'undefined' ) {
            items.forEach(function ( item ) {
                _this.push(item);
            });
        }
    }

    COSA.Util.deriveOverride(Array, ArrayValueBase, {
        type:'',
        namespace:'',
        namespaceHash:'',
        requireTypeDef:false,
        serialize: function ( sc ) {
            if ( typeof sc == 'undefined' ) {
                throw new Error(COSA.Const.Error.SC_UNDEFINED);
            }

            var ltr = sc.letter(this.namespaceHash);

            if ( typeof ltr == 'undefined' ) {
                ltr = sc.register(this.namespace);
                sc.pushParent(ltr.ns(), this.namespace);
            }

            if ( this.requireTypeDef ) {
                sc.pushParent('i:type', ltr.tag(this.type))
            }

            return this.getTemplate( sc, ltr );
        },
        getTemplate: function ( sc, ltr ) { },
        toKeyValuePair: function ( key ) {
            return new COSA.KeyValuePair(key, this);
        },
        validate: function () {}
    });

    //endregion ArrayValueBase

    //region EntityReferenceCollection

    function EntityReferenceCollection ( items ) {
        ArrayValueBase.call(this, items);
    }

    COSA.Util.deriveOverride(ArrayValueBase, EntityReferenceCollection, {
        type: 'EntityReferenceCollection',
        requireTypeDef: true,
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        validate: function () {
            this.forEach(function ( er ) {
                (new COSA.ValueChecker(er))
                    .defined()
                    .instanceOf(COSA.EntityReference)
                    .valid();
            });
        },
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( item ) {
                return {
                    tag: ltr.tag('EntityReference'),
                    nodes: item.serialize(sc)
                };
            });
        },
        deserialize: function ( node ) {
            var items = node.nodes('EntityReference'),
                _this = this;

            items.forEach(function ( item ) {
                _this.push((new COSA.EntityReference()).deserialize(item));
            });

            return this;
        }
    });

    //endregion EntityReferenceCollection

    //region ColumnSet

    function ColumnSet ( columns ) {
        ArrayValueBase.call(this, columns);
    }

    COSA.Util.deriveOverride(ArrayValueBase, ColumnSet, {
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        type:'ColumnSet',
        requireTypeDef:true,
        validate: function () {
            if ( this.length > 0 ) {
                this.forEach(function (item) {
                    new COSA.ValueChecker(item)
                        .defined()
                        .typeOf('string');
                });
            }
        },
        getTemplate: function ( sc, ltr ) {
            var allColumnsNode = {
                    tag: ltr.tag('AllColumns'),
                    val: this.length === 0
                },
                columnsNode = {
                    tag: ltr.tag('Columns')
                };

            sc.snapshot();
            if ( this.length > 0 ) {
                columnsNode.nodes = (new StringCollection(this)).serialize(sc);
                sc.appendToParent(columnsNode);
            }
            sc.restore();

            return [ allColumnsNode, columnsNode ];
        }
    });

    //endregion ColumnSet

    //region OrganizationRequestCollection

    /**
     * Organization request collection
     * @param requests
     * @memberOf Request
     * @constructor OrganizationRequestCollection
     */
    function OrganizationRequestCollection ( requests ) {
        ArrayValueBase.call(this, requests);
    }

    COSA.Util.deriveOverride(ArrayValueBase, OrganizationRequestCollection, {
        type: 'OrganizationRequestCollection',
        namespace: 'http://schemas.microsoft.com/xrm/2012/Contracts',
        requireTypeDef: true,
        validate: function () {
            if ( this.length > 0 ) {
                this.forEach(function ( request ) {
                    (new COSA.ValueChecker(request))
                        .defined()
                        .instanceOf(COSA.RequestBase)
                        .valid();
                });
            }
        },
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( item ) {
                return (new COSA.Request.OrganizationRequestWrapper(item)).serialize(sc);
            });
        }
    });

    //endregion OrganizationRequestCollection

    //region MS200310SerializationArray

    function MS200310SerializationArray ( values, type ) {
        ArrayValueBase.call(this, values);
    }

    COSA.Util.deriveOverride(ArrayValueBase, MS200310SerializationArray, {
        type:'',
        namespace:'http://schemas.microsoft.com/2003/10/Serialization/Arrays',
        requireTypeDef:false
    });

    //endregion MS200310SerializationArray

    //region AnyTypeCollection

    function AnyTypeCollection ( values ) {
        MS200310SerializationArray.call(this, values);
    }

    COSA.Util.deriveOverride(MS200310SerializationArray, AnyTypeCollection, {
        type:'AnyTypeArray',
        validate: function () {},
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( item ) {
                var res = { tag: ltr.tag('anyType') };

                sc.snapshot();

                res.val = item.serialize(sc);
                sc.appendToParent(res);

                sc.restore();

                return res;
            });
        }
    });

    //endregion AnyTypeCollection

    //region StringCollection

    function StringCollection ( values ) {
        MS200310SerializationArray.call(this, values);
    }

    COSA.Util.deriveOverride(MS200310SerializationArray, StringCollection, {
        type:'String',
        'getTemplate': function ( sc, ltr ) {
            return this.map(function (item) {
                return {tag: ltr.tag('string'), val: item};
            });
        },
        'deserialize': function ( node ) {
            var stringNodes = node.nodes('string'),
                _this = this;
            stringNodes.forEach(function ( stringNode ) {
                _this.push(stringNode.text());
            });
        }
    });



    //endregion StringCollection

    //region IntCollection

    function IntCollection ( items ) {
        MS200310SerializationArray.call(this, items);
    }

    COSA.Util.deriveOverride(MS200310SerializationArray, IntCollection, {
        type: 'Int',
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( item ) {
                return { tag: ltr.tag('int'), val: item.toString() };
            });
        },
        validate: function () {}
    });

    //endregion IntCollection

    //region RequestParameterCollection

    function RequestParametersCollection ( parameters ) {
        this.parameters = parameters;

        this.namespace = 'http://schemas.datacontract.org/2004/07/System.Collections.Generic';
        this.namespaceHash = COSA.Util.getHashString(this.namespace);
    }

    RequestParametersCollection.prototype.serialize = function ( sc ) {
        var ltr = sc.letter(this.namespaceHash),
            paramPairs = [], rs;

        if ( typeof ltr == 'undefined' ) {
            ltr = sc.register(this.namespace);
        }
        sc.pushParent(ltr.ns(), this.namespace);

        sc.snapshot();
        for ( var parameterKey in this.parameters ) {
            rs = this.parameters[parameterKey]
                .toKeyValuePair(parameterKey)
                .serialize(sc);

            paramPairs.push(rs);
        }
        sc.restore();

        return paramPairs;
    };

    //endregion RequestParameterCollection

    //region KeyValuePairCollection

    function KeyValuePairCollection ( pairs, namespace, wrapper ) {
        Array.call(this);
        this.namespace = namespace;
        this.namespaceHash = typeof namespace != 'undefined' ? COSA.Util.getHashString(namespace) : '';
        this.wrapper = wrapper;

        for ( var prp in pairs ) {
            this.push(pairs[prp].toKeyValuePair(prp));
        }
    }

    COSA.Util.derivePrototype(Array, KeyValuePairCollection);

    KeyValuePairCollection.prototype.serialize = function ( sc ) {
        var items = this.map(function ( pair ) {
            return pair.serialize(sc);
        }), wrp, ltr;

        if ( typeof this.wrapper != 'undefined' ) {
            ltr = sc.letter(this.namespaceHash);
            if ( typeof ltr == 'undefined' ) {
                ltr = sc.register(this.namespace);
            }
            wrp = { tag: ltr.tag(wrapper), nodes: items };
        }
        else {
            wrp = items;
        }

        return wrp;
    };

    //endregion KeyValuePairCollection

    //region ConditionsCollection

    function ConditionsCollection ( conditions ) {
        ArrayValueBase.call(this, conditions);
    }

    COSA.Util.deriveOverride(ArrayValueBase, ConditionsCollection, {
        type:'Conditions',
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( cnd ) { return cnd.serialize(sc); });
        }
    });

    //endregion ConditionsCollection

    //region FiltersCollection

    function FiltersCollection ( filters ) {
        ArrayValueBase.call(this, filters);
    }

    COSA.Util.deriveOverride(ArrayValueBase, FiltersCollection, {
        type: 'Filters',
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        getTemplate: function ( sc, ltr ) {
            return this.map( function ( filter ) {
                return {
                    tag: ltr.tag('FilterExpression'),
                    nodes: filter.serialize(sc)
                };
            });
        }
    });

    //endregion FiltersCollection

    //region OrderExpressionCollection

    function OrderExpressionCollection ( orders ) {
        ArrayValueBase.call(this, orders);
    }

    COSA.Util.deriveOverride(ArrayValueBase, OrderExpressionCollection, {
        type: 'Orders',
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( order ) { return order.serialize(sc); });
        }
    });

    //endregion OrderExpressionCollection

    //region LinkEntitiesCollection

    function LinkEntitiesCollection ( linkEntities ) {
        ArrayValueBase.call(this, linkEntities);
    }

    COSA.Util.deriveOverride(ArrayValueBase, LinkEntitiesCollection, {
        type: 'LinkEntities',
        namespace: 'http://schemas.microsoft.com/xrm/2011/Contracts',
        getTemplate: function ( sc, ltr ) {
            return this.map( function ( item ) {
                return {
                    tag: ltr.tag('LinkEntity'),
                    nodes: item.serialize(sc)
                };
            });
        }
    });

    //endregion LinkEntitiesCollection

    //region ArrayOfGuid

    function ArrayOfGuid ( guids ) {
        ArrayValueBase.call(this, guids);
    }

    COSA.Util.deriveOverride(ArrayValueBase, ArrayOfGuid, {
        type: 'ArrayOfguid',
        namespace: 'http://schemas.microsoft.com/2003/10/Serialization/Arrays',
        requireTypeDef: true,
        getTemplate: function ( sc, ltr ) {
            return this.map( function ( item ) {
                return {
                    tag: ltr.tag('guid'),
                    val: item
                };
            });
        }
    });

    //endregion ArrayOfGuid

    //region MetadataConditionCollection

    function MetadataConditionCollection ( items ) {
        ArrayValueBase.call(this, items);
    }

    COSA.Util.deriveOverride(ArrayValueBase, MetadataConditionCollection, {
        type: 'MetadataCondition',
        namespace: 'http://schemas.microsoft.com/xrm/2011/Metadata/Query',
        getTemplate: function ( sc, ltr ) {
            return this.map(function ( item ) {
                return {
                    tag: ltr.tag('MetadataConditionExpression'),
                    nodes: item.serialize(sc)
                };
            });
        }
    });

    //endregion MetadataConditionCollection

    //region EntityCollection
    /**
     * EntityCollection
     * @param {COSA.BusinessEntity[]} items Initial items
     * @prop {string} entityName Entity logical name
     * @prop {number} minActiveRowVersion Min active row version
     * @prop {boolean} moreRecords More records
     * @prop {string} pagingCookie Paging cookie
     * @prop {number} totalRecordCount Total Record Count
     * @prop {boolean} totalRecordCountLimitExceeded Total record count limit exceeded
     * @memberOf COSA
     * @constructor EntityCollection
     */
    function EntityCollection ( items ) {
        ArrayValueBase.call(this, items);

        this.entityName = '';
        this.minActiveRowVersion = -1;
        this.moreRecords = false;
        this.pagingCookie = null;
        this.totalRecordCount = -1;
        this.totalRecordCountLimitExceeded = false;
    }

    COSA.Util.deriveOverride(ArrayValueBase, EntityCollection, {
        type:'EntityCollection',
        deserialize: function ( node ) {
            var itms = node.namedNodes(),
                entityNodes = itms.Entities.nodes('Entity'),
                _this = this;

            (new COSA.Xml.TypedNodeMapper(this))
                .useMap({
                    'entityName': 'string:EntityName',
                    'minActiveRowVersion': 'int:MinActiveRowVersion',
                    'moreRecords': 'boolean:MoreRecords',
                    'pagingCookie': 'string:PagingCookie',
                    'totalRecordCount': 'int:TotalRecordCount',
                    'totalRecordCountLimitExceeded': 'boolean:TotalRecordCountLimitExceeded'
                })
                .applyValues(itms);

            entityNodes.forEach( function ( entity ) {
                _this.push((new COSA.BusinessEntity()).deserialize(entity));
            });

            return this;
        }
    });

    //endregion EntityCollection

    exports.ColumnSet = ColumnSet;
    exports.OrganizationRequestCollection = OrganizationRequestCollection;
    exports.RequestParametersCollection = RequestParametersCollection;
    exports.KeyValuePairCollection = KeyValuePairCollection;
    exports.ConditionsCollection = ConditionsCollection;
    exports.OrderExpressionCollection = OrderExpressionCollection;
    exports.LinkEntitiesCollection = LinkEntitiesCollection;
    exports.AnyTypeCollection = AnyTypeCollection;
    exports.FiltersCollection = FiltersCollection;
    exports.EntityReferenceCollection = EntityReferenceCollection;
    exports.ArrayOfGuid = ArrayOfGuid;
    exports.StringCollection = StringCollection;
    exports.IntCollection = IntCollection;
    exports.MetadataConditionCollection = MetadataConditionCollection;
    exports.EntityCollection = EntityCollection;

})(COSA);